package com.sakura.mall.generator.service.impl;

import com.sakura.mall.generator.config.GeneratorConfig;
import com.sakura.mall.generator.model.NameModel;
import com.sakura.mall.generator.model.TableInfo;
import com.sakura.mall.generator.service.CodeGeneratorService;
import com.sakura.mall.generator.service.DatabaseService;
import com.sakura.mall.generator.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.Velocity;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.tools.generic.DateTool;
import org.springframework.stereotype.Service;

import java.io.File;
import java.io.IOException;
import java.io.StringWriter;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.*;

/**
 * 代码生成器服务实现
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CodeGeneratorServiceImpl implements CodeGeneratorService {

    private final DatabaseService databaseService;
    private final GeneratorConfig config;
    
    /**
     * 模板位置
     */
    private static final String TEMPLATE_PATH = "templates";
    
    /**
     * Controller模板
     */
    private static final String TEMPLATE_CONTROLLER = "java/controller/Controller.java.vm";
    
    /**
     * Entity模板
     */
    private static final String TEMPLATE_ENTITY = "java/domain/entity/Entity.java.vm";
    
    /**
     * AddRequest模板
     */
    private static final String TEMPLATE_ADD_REQUEST = "java/domain/request/AddRequest.java.vm";
    
    /**
     * PageRequest模板
     */
    private static final String TEMPLATE_PAGE_REQUEST = "java/domain/request/PageRequest.java.vm";
    
    /**
     * UpdateRequest模板
     */
    private static final String TEMPLATE_UPDATE_REQUEST = "java/domain/request/UpdateRequest.java.vm";
    
    /**
     * Vo模板
     */
    private static final String TEMPLATE_VO = "java/domain/vo/Vo.java.vm";
    
    /**
     * Mapper模板
     */
    private static final String TEMPLATE_MAPPER = "java/mapper/Mapper.java.vm";
    
    /**
     * Service接口模板
     */
    private static final String TEMPLATE_SERVICE = "java/service/Service.java.vm";
    
    /**
     * Service实现类模板
     */
    private static final String TEMPLATE_SERVICE_IMPL = "java/service/impl/ServiceImpl.java.vm";
    
    /**
     * EntityToVo转换器模板
     */
    private static final String TEMPLATE_CONVERT_ENTITY_TO_VO = "java/convert/EntityToVo.java.vm";
    
    /**
     * AddRequestToEntity转换器模板
     */
    private static final String TEMPLATE_CONVERT_ADD_REQUEST = "java/convert/AddRequestToEntity.java.vm";
    
    /**
     * UpdateRequestToEntity转换器模板
     */
    private static final String TEMPLATE_CONVERT_UPDATE_REQUEST = "java/convert/UpdateRequestToEntity.java.vm";
    
    /**
     * 前端API模板
     */
    private static final String TEMPLATE_VUE_API = "vue/Api.js.vm";
    
    /**
     * 前端Vue组件模板
     */
    private static final String TEMPLATE_VUE = "vue/Vue.vue.vm";
    
    /**
     * 初始化Velocity引擎
     */
    private VelocityEngine initVelocityEngine() {
        Properties properties = new Properties();
        properties.setProperty("resource.loader", "class");
        properties.setProperty("class.resource.loader.class", "org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader");
        properties.setProperty(Velocity.ENCODING_DEFAULT, "UTF-8");
        properties.setProperty(Velocity.INPUT_ENCODING, "UTF-8");
        return new VelocityEngine(properties);
    }
    
    /**
     * 构建Velocity上下文
     */
    private VelocityContext createContext(TableInfo tableInfo) {
        VelocityContext context = new VelocityContext();
        
        // 添加表信息
        context.put("tableName", tableInfo.getTableName());
        context.put("description", tableInfo.getTableComment());
        context.put("author", config.getAuthor());
        context.put("packageName", tableInfo.getPackageName());
        context.put("moduleName", config.getModuleName());
        
        // 表名相关
        NameModel nameModel = StringUtils.getNameModel(tableInfo.getTableName(), config.getTablePrefix());
        context.put("name", nameModel);
        
        // 字段信息
        context.put("primaryKey", tableInfo.getPrimaryKey());
        context.put("fields", tableInfo.getColumns());
        
        // 工具类
        context.put("date", new DateTool());
        
        return context;
    }
    
    /**
     * 获取所有模板
     */
    private List<String> getTemplates() {
        List<String> templates = new ArrayList<>();
        
        // Java模板
        templates.add(TEMPLATE_CONTROLLER);
        templates.add(TEMPLATE_ENTITY);
        templates.add(TEMPLATE_ADD_REQUEST);
        templates.add(TEMPLATE_PAGE_REQUEST);
        templates.add(TEMPLATE_UPDATE_REQUEST);
        templates.add(TEMPLATE_VO);
        templates.add(TEMPLATE_MAPPER);
        templates.add(TEMPLATE_SERVICE);
        templates.add(TEMPLATE_SERVICE_IMPL);
        
        // 转换器模板
        templates.add(TEMPLATE_CONVERT_ENTITY_TO_VO);
        templates.add(TEMPLATE_CONVERT_ADD_REQUEST);
        templates.add(TEMPLATE_CONVERT_UPDATE_REQUEST);
        
        // 前端模板
        templates.add(TEMPLATE_VUE_API);
        templates.add(TEMPLATE_VUE);
        
        return templates;
    }
    
    /**
     * 获取文件名
     */
    private String getFileName(String template, TableInfo tableInfo) {
        String upperCamelName = StringUtils.toPascalCase(
                StringUtils.removePrefix(tableInfo.getTableName(), config.getTablePrefix()));
        String lowerCamelName = StringUtils.toCamelCase(
                StringUtils.removePrefix(tableInfo.getTableName(), config.getTablePrefix()));
        
        String packagePath = config.getPackageName().replace(".", "/");
        String moduleName = config.getModuleName();
        String path = config.getOutputDir() + "/" + (moduleName != null ? moduleName + "/" : "") + packagePath + "/" + lowerCamelName + "/";
        
        return switch (template) {
            case TEMPLATE_CONTROLLER -> 
                path + "controller/" + upperCamelName + "Controller.java";
                
            case TEMPLATE_ENTITY -> 
                path + "domain/entity/" + upperCamelName + "Entity.java";
                
            case TEMPLATE_ADD_REQUEST -> 
                path + "domain/request/Add" + upperCamelName + "Request.java";
                
            case TEMPLATE_PAGE_REQUEST -> 
                path + "domain/request/" + upperCamelName + "PageRequest.java";
                
            case TEMPLATE_UPDATE_REQUEST ->
                path + "domain/request/Update" + upperCamelName + "Request.java";
                
            case TEMPLATE_VO ->
                path + "domain/vo/" + upperCamelName + "Vo.java";
                
            case TEMPLATE_MAPPER ->
                path + "mapper/" + upperCamelName + "Mapper.java";
                
            case TEMPLATE_SERVICE ->
                path + "service/" + upperCamelName + "Service.java";
                
            case TEMPLATE_SERVICE_IMPL ->
                path + "service/impl/" + upperCamelName + "ServiceImpl.java";
            
            // 转换器文件
            case TEMPLATE_CONVERT_ENTITY_TO_VO ->
                path + "domain/convert/" + upperCamelName + "EntityTo" + upperCamelName + "Vo.java";
                
            case TEMPLATE_CONVERT_ADD_REQUEST ->
                path + "domain/convert/Add" + upperCamelName + "RequestTo" + upperCamelName + "Entity.java";
                
            case TEMPLATE_CONVERT_UPDATE_REQUEST ->
                path + "domain/convert/Update" + upperCamelName + "RequestTo" + upperCamelName + "Entity.java";
                
            case TEMPLATE_VUE_API ->
                config.getOutputDir() + "/vue/" + StringUtils.toHyphenCase(upperCamelName) + "/api.js";
                
            case TEMPLATE_VUE ->
                config.getOutputDir() + "/vue/" + StringUtils.toHyphenCase(upperCamelName) + "/index.vue";
                
            default -> null;
        };
    }
    
    /**
     * 渲染模板
     */
    private String render(VelocityEngine engine, String templatePath, VelocityContext context) {
        StringWriter sw = new StringWriter();
        Template template = engine.getTemplate(templatePath, "UTF-8");
        template.merge(context, sw);
        return sw.toString();
    }
    
    /**
     * 生成代码文件
     */
    private void writeToFile(String content, String filePath) throws IOException {
        File file = new File(filePath);
        // 创建父目录
        if (!file.getParentFile().exists()) {
            file.getParentFile().mkdirs();
        }
        Files.write(Paths.get(filePath), content.getBytes());
    }
    
    @Override
    public List<String> generate(List<String> tableNames) {
        List<String> generatedFiles = new ArrayList<>();
        
        for (String tableName : tableNames) {
            generatedFiles.addAll(generate(tableName));
        }
        
        return generatedFiles;
    }
    
    @Override
    public List<String> generate(String tableName) {
        // 获取表信息
        TableInfo tableInfo = databaseService.getTableInfo(tableName);
        if (tableInfo == null) {
            return new ArrayList<>();
        }
        
        List<String> generatedFiles = new ArrayList<>();
        VelocityEngine engine = initVelocityEngine();
        VelocityContext context = createContext(tableInfo);
        
        try {
            // 获取所有模板
            List<String> templates = getTemplates();
            
            // 生成代码
            for (String template : templates) {
                // 渲染模板
                String content = render(engine, TEMPLATE_PATH + "/" + template, context);
                
                // 获取输出文件路径
                String filePath = getFileName(template, tableInfo);
                if (filePath != null) {
                    // 写入文件
                    writeToFile(content, filePath);
                    generatedFiles.add(filePath);
                }
            }
        } catch (Exception e) {
            log.error("生成代码失败", e);
        }
        
        return generatedFiles;
    }
    
    @Override
    public Map<String, String> preview(String tableName) {
        // 获取表信息
        TableInfo tableInfo = databaseService.getTableInfo(tableName);
        if (tableInfo == null) {
            return new HashMap<>();
        }
        
        Map<String, String> result = new HashMap<>();
        VelocityEngine engine = initVelocityEngine();
        VelocityContext context = createContext(tableInfo);
        
        try {
            // 获取所有模板
            List<String> templates = getTemplates();
            
            // 生成代码
            for (String template : templates) {
                // 渲染模板
                String content = render(engine, TEMPLATE_PATH + "/" + template, context);
                
                // 文件路径作为Key
                String key = getFileName(template, tableInfo);
                if (key != null) {
                    result.put(key, content);
                }
            }
        } catch (Exception e) {
            log.error("预览代码失败", e);
        }
        
        return result;
    }
    
    @Override
    public List<String> getAllTableNames() {
        return databaseService.getAllTableNames();
    }
} 